#pragma once

#include"Socket.hpp"
#include"Log.hpp"
#include<iostream>
#include<memory>
#include<pthread.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<fstream>
#include<string.h>
#include<vector>
#include<sstream>
#include<unordered_map>


using namespace std;

const int defaultport = 8080;
const string lyyroot ="./lyyroot/";
const string sep = "\r\n";
const string homepage = "index.html";

class ThreadData
{
public:
    ThreadData(int fd)
    :sockfd(fd)
    {}

public:
    int sockfd;
};

class HttpRequest
{
public:
    void Deserialize(string req)
    {
        while(true)
        {
            size_t pos = req.find(sep);
            if(pos == string::npos) break;
            string temp = req.substr(0, pos);
            if(temp.empty()) break;

            req_header.push_back(temp);
            req.erase(0, pos + sep.size());
        }
        text = req;
    }

    void Parse()
    {
        stringstream ss(req_header[0]);
        ss >> method >> url >>http_version;
        file_path = lyyroot;
        if(url == "/" || url == "/index.trml") file_path += homepage;
        else file_path += url;

        auto pos = file_path.rfind(".");
        if(pos == string::npos) suffix = ".html";
        else suffix = file_path.substr(pos);
    }


    void DebugPrint()
    {
        for(auto &line : req_header)
        {
            cout<<"------------------------"<<endl;
            cout<<line<<"\n";
        }
        cout<<"method: "<<method<<endl;
        cout<<"url: "<<url<<endl;
        cout<<"http_version: "<<http_version<<endl;
        cout<<"file_path: "<<file_path<<endl;
        cout<<"text: "<<text<<endl;
    }
public:
    vector<string> req_header;
    string method;
    string url;
    string http_version;
    string text;
    string file_path;
    string suffix;
};

class HttpServer
{
public:
    HttpServer(int port = defaultport)
    {}

    bool Start()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        while(true)
        {
            string clientip;
            uint16_t clientport;
            int sockfd = _listensock.Accept(&clientip, &clientport);
            if(sockfd < 0) continue;
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd);
            td->sockfd = sockfd;
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }
    static string ReadHtmlContent(const string &path)
    {
        //这里这样读取在纯文本下没有问题，但是如果是二进制文件就会出错了
        // ifstream in(path);
        // if(!in.is_open()) return "";
        // string line;
        // string content;
        // while(getline(in, line));
        // {
        //     content += line;
        // }
        ifstream in(path, ios::binary);
        if(!in.is_open()) return "";
        
        in.seekg(0, ios_base::end);
        auto len = in.tellg();
        in.seekg(0, ios_base::beg);

        string content;
        content.resize(len);
        in.read((char *)content.c_str(), content.size());
        in.close();
        return content;
    }

    static string SuffixToDesc(const string& suffix, unordered_map<string, string> &content_types)
    {
        auto iter = content_types.find(suffix);
        if(iter == content_types.end()) return content_types[".html"];
        else return content_types[suffix];
    }


    static void HanderHttp(int sockfd)
    {
        char buffer[10240];
        unordered_map<string, string> content_types;
        content_types.insert({".html", "text/html"});
        content_types.insert({".png", "image/png"});

        ssize_t n = read(sockfd, buffer, sizeof(buffer)-1);
        if(n > 0)
        {
            buffer[n] = 0;
            cout<<buffer;
            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse(); 
            // req.DebugPrint();
            // string url = "";
            // string path = lyyroot;
            // path += url;

            //返回响应
            string text;
            bool ok = true;
            text = ReadHtmlContent(req.file_path);
            if(text.empty())
            {
                ok = false;
                string err_html = lyyroot;
                // err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }
            string response_line;
            if(ok)
                response_line = "HTTP/1.0 200 OK\r\n";
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";
            string response_header = "Content-length: ";
            response_header += to_string(text.size());
            response_header += "\r\n";
            response_header += "Content-Length: ";
            response_header += SuffixToDesc(req.suffix, content_types);
            response_header += "\r\n";
            response_header += "Set-Cookie: name=haha&&password=1234";
            response_header += "\r\n";

            string block_line = "\r\n";
            string response = response_line;
            response += response_header;
            response += block_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }

    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData*>(args);
        HanderHttp(td->sockfd);
        delete td;
        return nullptr;
    }

    ~HttpServer()
    {}
private:
    Sock _listensock;
    uint16_t _port;
};